package cn.admin.remoting.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;

public abstract class SharedResourcesInit<V> {

    private static final int RETRY_TIMES = 2;
    private static final String SINGLE_RSC_KEY = "single_resource";
    private ConcurrentHashMap<String,SimpleFutureTask> initRightSelect = new ConcurrentHashMap<>();

    public V getResource() throws ResourceInvalidException, InterruptedException, ExecutionException {

        if (!this.isSingletonResource()) {
            throw new UnsupportedOperationException();
        } else {
            return this.innerGetResource(SINGLE_RSC_KEY,null);
        }

    }

    public V getResource(String key,Object attachment) throws ResourceInvalidException,
            InterruptedException, ExecutionException {
        if (this.isSingletonResource()) {
            throw new UnsupportedOperationException();
        } else {
            return this.innerGetResource(key,attachment);
        }
    }

    public List<String> keys() {
        List<String> keys = null;

        if (!this.isSingletonResource()) {
            keys = new ArrayList<>(this.initRightSelect.keySet());
        }
        return keys;
    }

    public Map<String,V> resourcesSnapshot() throws ExecutionException, InterruptedException {

        Map<String,V> snapshot = new HashMap<>();
        for (Map.Entry<String,SimpleFutureTask> entry : this.initRightSelect.entrySet()) {
            String key = entry.getKey();
            V resource = entry.getValue().get();

            try {
                this.validate(resource);
                snapshot.put(key,resource);
            } catch (ResourceInvalidException e) {
                e.printStackTrace();
            }
        }
        return snapshot;
    }

    private V innerGetResource(String key,Object attachment) throws ResourceInvalidException,
            InterruptedException,ExecutionException {

        V resource = null;

        for (int i = 0;i < RETRY_TIMES && resource == null; ++i) {
            SimpleFutureTask initTask = this.initRightSelect.get(key);
            if (initTask == null) {
                initTask = this.initRightSelect.putIfAbsent(key,new SimpleFutureTask(key,attachment));
                if (initTask == null) {
                    initTask = this.initRightSelect.get(key);
                    initTask.run();
                }
            }

            try {
                resource = initTask.get();
                if (resource == null) {
                    throw new ResourceInvalidException("创建的共享资源[" + key + "]为NULL.");
                }
                this.validate(resource);
            } catch (InterruptedException e) {
                this.initRightSelect.remove(key,initTask);
                throw e;
            } catch (ExecutionException e) {
                this.initRightSelect.remove(key,initTask);
                if (i + 1 >= RETRY_TIMES) {
                    throw e;
                }

                resource = null;
            }
        }
        return resource;
    }

    protected abstract boolean isSingletonResource();

    protected V initResource() throws Exception {
        if (!this.isSingletonResource()) {
            throw new UnsupportedOperationException();
        } else {
            return null;
        }
    }

    protected V keyedInitResource(String key,Object attachment) throws Exception {

        if (this.isSingletonResource()) {
            throw new UnsupportedOperationException();
        } else {
            return null;
        }

    }

    protected abstract void validate(V result) throws ResourceInvalidException;

    private class SimpleFutureTask {

        private final String key;
        private final Object attachment;
        private volatile boolean isDone = false;
        private V result;
        private ExecutionException exception;

        SimpleFutureTask(String key,Object attachment) {
            this.key = key;
            this.attachment = attachment;
        }

        public synchronized V get() throws InterruptedException, ExecutionException {

            while (!this.isDone) {
                this.wait();
            }

            if (this.exception != null) {
                throw this.exception;
            } else {
                return this.result;
            }

        }

        public void run() {

            try {
                if (SharedResourcesInit.this.isSingletonResource()) {
                    this.result = SharedResourcesInit.this.initResource();
                } else {
                    this.result = SharedResourcesInit.this.keyedInitResource(this.key,this.attachment);
                }
            } catch (Exception e) {
                this.exception = new ExecutionException(e);
            }

            synchronized (this) {
                this.isDone = true;
                this.notifyAll();
            }
        }

    }
}
